#include <assert.h>
#include <irrlicht.h>
#include "demo.h"
#include "fx_kimble.h"


using namespace irr;
using namespace irr::core;
using namespace irr::scene;


static ISceneManager *sSceneManager = 0;
static ISceneNode *sKimble = 0;
static ISceneNode *sKupla = 0;
static ISceneNode *sSormi = 0;
static ISceneNode *sSauvat = 0;
static ISceneNode *sNoppa1 = 0;
static ISceneNode *sNoppa2 = 0;

struct PosRot
{
    vector3df pos;
    vector3df rot; // euler angles used by irr
};

static array<PosRot> sPath;

static vector3df sStartCamPos(0, 0, 0);


bool KimbleFx::init()
{
    if (!gIrrDevice || !gIrrDevice->getSceneManager())
        return false;

    //bool zipRes = gIrrDevice->getFileSystem()->addFileArchive("data_oldies_hallintaa.zip", false, false, io::EFAT_ZIP);
    //assert(zipRes);

    sSceneManager = gIrrDevice->getSceneManager()->createNewSceneManager();

    ISceneManager *sceneManager = sSceneManager;

    sceneManager->loadScene("data/oldies/kimble.irr");

    //ISceneNode *light = sceneManager->getSceneNodeFromType(scene::ESNT_LIGHT);

    sKimble = sceneManager->getSceneNodeFromName("pelilauta");
	sKupla = sceneManager->getSceneNodeFromName("kupla");
	sSormi = sceneManager->getSceneNodeFromName("sormi");
	sNoppa1 = sceneManager->getSceneNodeFromName("noppa1");
	sNoppa2 = sceneManager->getSceneNodeFromName("noppa2");
	sSauvat = sceneManager->getSceneNodeFromName("sauvat");
	sSauvat->setVisible(false);

    sceneManager->addCameraSceneNode();
    sceneManager->getActiveCamera()->setPosition(sStartCamPos);
	vector3df cameraTarget = sKupla->getPosition();
	cameraTarget.Y -= 20;
	sceneManager->getActiveCamera()->setTarget(cameraTarget);

	// pick path nodes
    {
        int nextNodeId = 0;
        bool more;
        do
        {
            more = false;
            ISceneNode *sn = sceneManager->getSceneNodeFromId(nextNodeId++);
            if (sn)
            {
                PosRot pr;
                pr.pos = sn->getPosition();
                pr.rot = sn->getRotation();
                sPath.push_back(pr);
                more = true;				
            }
        } while (more);
    }
	
	mInitialized = true;
    return true;
}

static s32 sClampSplineIndex(s32 idx, s32 size)
{
	//return ( idx<0 ? size+idx : ( idx>=size ? idx-size : idx ) );  // loop
    if (idx < 0)
        idx = 0;
    //if (idx >= size)
    //    idx = size - 1;
    if (idx >= size)
        idx = size - 3 + idx % 3; // loop last 3 control points
    return idx;
}

// normalizedPosition should be time in range [0..1]
static core::vector3df sInterpolatePosition(const array<PosRot> &keys, float normalizedPosition, float tightness = 0.5f)
{
    core::vector3df result(0, 0, 0);

    const int pSize = (int)keys.size();
    if (pSize == 0)
        return result;

    // Spline-interpolate position
    // (Spline interpolation code is taken from Irrlicht's CSceneNodeAnimatorFollowSpline)

    const f32 dt = normalizedPosition * pSize;
    const f32 u = core::fract(dt);
    const s32 idx = core::floor32(dt); // % pSize; - disabled wrap-around...

    s32 idx0 = sClampSplineIndex(idx - 1, pSize);
    s32 idx1 = sClampSplineIndex(idx, pSize);
    s32 idx2 = sClampSplineIndex(idx + 1, pSize);
    s32 idx3 = sClampSplineIndex(idx + 2, pSize);
    const core::vector3df p0 = keys[idx0].pos;
    const core::vector3df p1 = keys[idx1].pos;
    const core::vector3df p2 = keys[idx2].pos;
    const core::vector3df p3 = keys[idx3].pos;

    // hermite polynomials
    const f32 h1 = 2.0f * u * u * u - 3.0f * u * u + 1.0f;
    const f32 h2 = -2.0f * u * u * u + 3.0f * u * u;
    const f32 h3 = u * u * u - 2.0f * u * u + u;
    const f32 h4 = u * u * u - u * u;

    // tangents
    const core::vector3df t1 = ( p2 - p0 ) * tightness;
    const core::vector3df t2 = ( p3 - p1 ) * tightness;

    // interpolated point
    result = p1 * h1 + p2 * h2 + t1 * h3 + t2 * h4;
    
    return result;
}


void KimbleFx::render(double time, int timeMs)
{   
	float t = (float)(time - mFxStartTime);
	//vector3df eulerAngles(0.0, 0.0, -(float)t / 40.0f);	
    float nt = (float)(t / (mFxEndTime - mFxStartTime));
	vector3df pos = sInterpolatePosition(sPath, nt);

    vector3df camPos = sStartCamPos;
    camPos += vector3df(cosf(nt), 1, sinf(nt)) * sSmoothStep(nt, 0, 0.25f) * vector3df(18, 18, -18);
	sSceneManager->getActiveCamera()->setPosition(camPos);

    sSormi->setVisible(true);
    sSormi->setPosition(pos);

    sSormi->setRotation(sSmoothPulse(nt, 0.3f, 0.4f, 0.4f, 0.5f) * vector3df(0, 0, -20.0f));

    sKupla->setPosition(sBoxPulse(nt, 0.4f, 0.42f, 0.42f, 0.44f) * vector3df(0, -0.2f, 0));

    sNoppa1->setRotation(sBoxStep(nt, 0.44f, 0.46f) * vector3df(0, 0, -90.0f));
    sNoppa2->setRotation(sBoxStep(nt, 0.44f, 0.46f) * vector3df(0, 90.f, 0));

	sSceneManager->drawAll();
}


void KimbleFx::deinit()
{
    sSceneManager->drop();
    mInitialized = false;
}
